'use strict';

const Cesium = require('cesium');
const addExtensionsUsed = require('./addExtensionsUsed');
const addExtensionsRequired = require('./addExtensionsRequired');
const addToArray = require('./addToArray');
const ForEach = require('./ForEach');

const defined = Cesium.defined;

module.exports = moveTechniquesToExtension;

/**
 * Move glTF 1.0 material techniques to glTF 2.0 KHR_techniques_webgl extension.
 *
 * @param {Object} gltf A javascript object containing a glTF asset.
 * @returns {Object} The updated glTF asset.
 *
 * @private
 */
function moveTechniquesToExtension(gltf) {
    const techniquesLegacy = gltf.techniques;
    const mappedUniforms = {};
    const updatedTechniqueIndices = {};
    const seenPrograms = {};
    if (defined(techniquesLegacy)) {
        const extension = {
            programs: [],
            shaders: [],
            techniques: []
        };

        // Some 1.1 models have a glExtensionsUsed property that can be transferred to program.glExtensions
        const glExtensions = gltf.glExtensionsUsed;
        delete gltf.glExtensionsUsed;

        ForEach.technique(gltf, function (techniqueLegacy, techniqueId) {
            const technique = {
                name: techniqueLegacy.name,
                program: undefined,
                attributes: {},
                uniforms: {}
            };

            let parameterLegacy;
            ForEach.techniqueAttribute(techniqueLegacy, function (parameterName, attributeName) {
                parameterLegacy = techniqueLegacy.parameters[parameterName];
                technique.attributes[attributeName] = {
                    semantic: parameterLegacy.semantic
                };
            });

            ForEach.techniqueUniform(techniqueLegacy, function (parameterName, uniformName) {
                parameterLegacy = techniqueLegacy.parameters[parameterName];
                technique.uniforms[uniformName] = {
                    count: parameterLegacy.count,
                    node: parameterLegacy.node,
                    type: parameterLegacy.type,
                    semantic: parameterLegacy.semantic,
                    value: parameterLegacy.value
                };

                // Store the name of the uniform to update material values.
                if (!defined(mappedUniforms[techniqueId])) {
                    mappedUniforms[techniqueId] = {};
                }
                mappedUniforms[techniqueId][parameterName] = uniformName;
            });

            if (!defined(seenPrograms[techniqueLegacy.program])) {
                const programLegacy = gltf.programs[techniqueLegacy.program];

                const program = {
                    name: programLegacy.name,
                    fragmentShader: undefined,
                    vertexShader: undefined,
                    glExtensions: glExtensions
                };

                const fs = gltf.shaders[programLegacy.fragmentShader];
                program.fragmentShader = addToArray(extension.shaders, fs, true);

                const vs = gltf.shaders[programLegacy.vertexShader];
                program.vertexShader = addToArray(extension.shaders, vs, true);

                technique.program = addToArray(extension.programs, program);
                seenPrograms[techniqueLegacy.program] = technique.program;
            } else {
                technique.program = seenPrograms[techniqueLegacy.program];
            }

            // Store the index of the new technique to reference instead.
            updatedTechniqueIndices[techniqueId] = addToArray(extension.techniques, technique);
        });

        if (extension.techniques.length > 0) {
            if (!defined(gltf.extensions)) {
                gltf.extensions = {};
            }

            gltf.extensions.KHR_techniques_webgl = extension;
            addExtensionsUsed(gltf, 'KHR_techniques_webgl');
            addExtensionsRequired(gltf, 'KHR_techniques_webgl');
        }
    }

    ForEach.material(gltf, function (material) {
        if (defined(material.technique)) {
            const materialExtension = {
                technique: updatedTechniqueIndices[material.technique]
            };

            ForEach.objectLegacy(material.values, function (value, parameterName) {
                if (!defined(materialExtension.values)) {
                    materialExtension.values = {};
                }

                const uniformName = mappedUniforms[material.technique][parameterName];
                materialExtension.values[uniformName] = value;
            });

            if (!defined(material.extensions)) {
                material.extensions = {};
            }

            material.extensions.KHR_techniques_webgl = materialExtension;
        }

        delete material.technique;
        delete material.values;
    });

    delete gltf.techniques;
    delete gltf.programs;
    delete gltf.shaders;

    return gltf;
}
